package edu.uaskl.jqf.playground;

import edu.uaskl.jqf.model.Qubit;
import edu.uaskl.jqf.model.gates.util.Gates;
import edu.uaskl.jqf.model.matrix.MatrixComplex;

public class ue4 {
    public static void main(final String[] args) {
        d();
        // System.out.println(Gates.HADAMARD.tensor(Gates.HADAMARD.getSize(2)).tensor(Gates.HADAMARD.getSize(2))
        // .multiply(Qubit.q0.tensor(Qubit.q1).tensor(Qubit.q1)));
    }

    public static void temo() {
        System.out.println();
        System.out.println(Gates.HADAMARD.tensor(Qubit.q1));
        System.out.println(Gates.HADAMARD.tensor(Gates.IDENTITY.getSize(2)));

        System.out.println();
        System.out.println(Qubit.q0.tensor(Qubit.q1));
        System.out.println(Gates.SWAP.multiply(Qubit.q0.tensor(Qubit.q1)));

        System.out.println();
        System.out.println(Gates.HADAMARD.multiply(Qubit.q0).tensor(Qubit.q0));
        System.out.println(Gates.HADAMARD.tensor(Gates.IDENTITY.getSize(2)).multiply(Qubit.q0.tensor(Qubit.q0)));

        System.out.println();
    }

    public static void a1() {
        final MatrixComplex gatterV = Gates.HADAMARD.tensor(Gates.IDENTITY.getSize(2)).multiply(Gates.CNOT.getSize(4));
        System.out.println(gatterV.multiply(Qubit.q0.tensor(Qubit.q0)));
        System.out.println(gatterV.multiply(Qubit.q0.tensor(Qubit.q1)));
        System.out.println(gatterV.multiply(Qubit.q1.tensor(Qubit.q0)));
        System.out.println(gatterV.multiply(Qubit.q1.tensor(Qubit.q1)));

        final double invSqrt = 1.0 / Math.sqrt(2);
        final Qubit plus = new Qubit(invSqrt, invSqrt);
        final Qubit minus = new Qubit(invSqrt, -invSqrt);
        System.out.println(gatterV.multiply(Qubit.q0.tensor(plus)));
        System.out.println(gatterV.multiply(Qubit.q0.tensor(minus)));
        System.out.println(gatterV.multiply(plus.tensor(Qubit.q0)));
        System.out.println(gatterV.multiply(minus.tensor(Qubit.q0)));
    }

    public static void a1t() {
        final MatrixComplex gatterV = Gates.CNOT.getSize(4).multiply(Gates.HADAMARD.tensor(Gates.IDENTITY.getSize(2)));
        System.out.println(gatterV.multiply(Qubit.q0.tensor(Qubit.q0)));
        System.out.println(gatterV.multiply(Qubit.q0.tensor(Qubit.q1)));
        System.out.println(gatterV.multiply(Qubit.q1.tensor(Qubit.q0)));
        System.out.println(gatterV.multiply(Qubit.q1.tensor(Qubit.q1)));

        final double invSqrt = 1.0 / Math.sqrt(2);
        final Qubit plus = new Qubit(invSqrt, invSqrt);
        final Qubit minus = new Qubit(invSqrt, -invSqrt);
        System.out.println(gatterV.multiply(Qubit.q0.tensor(plus)));
        System.out.println(gatterV.multiply(Qubit.q0.tensor(minus)));
        System.out.println(gatterV.multiply(plus.tensor(Qubit.q0)));
        System.out.println(gatterV.multiply(minus.tensor(Qubit.q0)));
    }

    public static void a2() {
        final MatrixComplex singleHadamard = Gates.HADAMARD.tensor(Gates.IDENTITY.getSize(2));
        final MatrixComplex gatter = singleHadamard.multiply(Gates.CNOT.getSize(4)).multiply(singleHadamard);
        System.out.println(gatter.multiply(Qubit.q0.tensor(Qubit.q0)));
        System.out.println(gatter.multiply(Qubit.q0.tensor(Qubit.q1)));
        System.out.println(gatter.multiply(Qubit.q1.tensor(Qubit.q0)));
        System.out.println(gatter.multiply(Qubit.q1.tensor(Qubit.q1)));
    }

    public static void a3() {
        final MatrixComplex doubleHadamard = Gates.HADAMARD.tensor(Gates.HADAMARD.getSize(2));
        final MatrixComplex gatter = doubleHadamard.multiply(Gates.CNOT.getSize(4)).multiply(doubleHadamard);
        System.out.println(gatter);
        System.out.println(gatter.multiply(Qubit.q0.tensor(Qubit.q0)));
        System.out.println(gatter.multiply(Qubit.q0.tensor(Qubit.q1)));
        System.out.println(gatter.multiply(Qubit.q1.tensor(Qubit.q0)));
        System.out.println(gatter.multiply(Qubit.q1.tensor(Qubit.q1)));
    }

    public static void d() {
        final MatrixComplex q01 = Qubit.q0.tensor(Qubit.q1);

        final MatrixComplex H4 = Gates.HADAMARD.tensor(Gates.HADAMARD.getSize(2));
        final MatrixComplex U1 = new MatrixComplex(new double[][] { { 0, 1, 0, 0 }, { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } });
        System.out.println(H4.multiply(U1).multiply(H4).multiply(Qubit.q0.tensor(Qubit.q1)));

        final MatrixComplex U2 = new MatrixComplex(new double[][] { { 0, 1, 0, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 1 }, { 0, 0, 1, 0 } });
        System.out.println(H4.multiply(U2).multiply(H4).multiply(q01));
    }
}
